#ifndef MOE_POLINOMIAL_
#define MOE_POLINOMIAL_

#include "engine/Mutation.hh"
#include "problems/RealArraySolution.hh"
#include <iostream>

class PolinomialMutation : public Mutation {

private:

    double index;

public:

    PolinomialMutation(double index, double probability) : Mutation(probability) {
        this->index = index;
    }

    void setIndex(double index) {
        this->index = index;
    }

    double getIndex() {
        return index;
    }

protected:

    virtual void mutate(Solution *solution) {
        RealArraySolution *realSolution = (RealArraySolution *) solution;

        for (int i = 0; i < RealArraySolution::getSize(); i++) {
            if (Random::getInstance()->flip(probability)) {
                double value = realSolution->get(i);
                change(value, RealArraySolution::getLowerBounds()[i],
                              RealArraySolution::getHigherBounds()[i]);

                realSolution->set(i, value);
            }
        }

    }

private:

    void change(double &value, const double lowerLimit, const double higherLimit) {

        double delta1 = (value - lowerLimit) / (higherLimit - lowerLimit);

        double delta2 = (higherLimit - value) / (higherLimit - lowerLimit);

        double random = Random::getInstance()->getReal(0.0, 1.0);

        double delta_q = 0;

        if (random <= 0.5) {

            double temp = 2.0 * random + (1.0 - 2.0 * random) * pow (1.0 - delta1, index + 1.0);
            delta_q = pow(temp, 1.0 / (index + 1.0)) - 1.0;

        } else {

            double temp = 2.0 * (1.0 - random) + 2.0 * (random - 0.5) * pow (1.0 - delta2, index + 1.0);
            delta_q = 1.0 - pow(temp, 1.0 / (index + 1.0));

        }

        value = value + delta_q * (higherLimit - lowerLimit);
    }
};


#endif
